

#pragma once

#include "cybertron/DefinesCore.hpp"
#include "cybertron/network/Message.hpp"
#include "cybertron/network/SocketBase.hpp"
#include "SocketEventHandlerList.hpp"
#include <asio.hpp>
#include <mutex>
#include <list>

CYBERTRON_BEGIN

class AsioSocketAsync : public std::enable_shared_from_this<AsioSocketAsync>
{
public:
	AsioSocketAsync(
		std::shared_ptr<asio::io_context> pContext, 
		asio::ip::tcp::socket&& socket,
		ESocketStatus status,
		const std::string remoteIp,
		int remotePort);
	AsioSocketAsync(
		std::shared_ptr<asio::io_context> pContext,
		ESocketStatus status,
		const std::string remoteIp,
		int remotePort);
	~AsioSocketAsync();

	void setEventHandlers(std::shared_ptr<SocketEventHandlerList> pHandlers);

	void connect(asio::ip::tcp::resolver::results_type& endpoints, std::function<void()> onConnect);
	void startReceiving();
	void send(const Message& msg);
	void sendHeartbeat();
	void close(const char* reason);

	ESocketStatus getStatus() const;

	const std::string& getRemoteIp() const;
	int getRemotePort() const;
	unsigned int getLastMessageTime() const;

	[[deprecated("Only use for unit test")]]
	asio::ip::tcp::socket *getSocket() { return &mSocket; }
private:
	void doSend();
	void doReceiveHeader();
	void doReceiveBody();
	bool doSetClosed();

private:
	std::shared_ptr<SocketEventHandlerList> mEventHandlers;

	std::shared_ptr<asio::io_context> mpContext;

	std::string mSocketType;
	asio::ip::tcp::socket mSocket;
	std::string mRemoteIp;
	int mRemotePort;

	std::atomic<unsigned int> mLastMessageTime;
	Message mReceiveMessageHeader;
	Message mReceiveMessageProcessed;

	std::recursive_mutex mSendMessageQueueMutex;
	std::list<Message> mSendMessageQueue;

	mutable std::recursive_mutex mStatusMutex;
	ESocketStatus mStatus;
};

CYBERTRON_END
